package com.bappi.ui;

import java.util.List;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

public class Chart extends View implements OnTouchListener {
	private static final String TAG = "Touch";
	private List<ChartData> points;
	private static final float ITEM_INTERVAL = 30;
	private static final float UNIT_HEIGHT = 1;
	private static final int MIN_HEIGHT = 1000;
	private static final int MIN_WIDTH = 1000;
	

	private int lineWidth;
	private int lineHeight;
	private float initialOffsetX;
	private float initialOffsetY;
	private float fontOffset;
	private float largeCellValue;

	private float[] graphSrc;
	private float[] graphDst;
	private float[] axisSrc;
	private float[] axisDst;

	private Paint graphPaint;
	private Paint linePaint1;
	private Paint textPaintWhite;
	private Paint textPaintGreen;
	private boolean dimentionFound;

	private Matrix matrix = new Matrix();
	private Matrix savedMatrix = new Matrix();
	private static final int NONE = 0;
	private static final int DRAG = 1;
	private static final int ZOOM = 2;
	private int mode = NONE;
	private PointF start = new PointF();
	private PointF mid = new PointF();
	private float oldDist = 1f;

	public Chart(Context context) {
		super(context);
		init();
	}

	public Chart(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	public Chart(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init();
	}

	private void init() {
		graphPaint = new Paint();
		graphPaint.setAntiAlias(true);
		graphPaint.setColor(0xFF00FF00);
		graphPaint.setTextAlign(Align.LEFT);
		textPaintGreen = new Paint();
		textPaintGreen.setAntiAlias(true);
		textPaintGreen.setColor(0xFF00FF00);
		textPaintGreen.setTextAlign(Align.RIGHT);

		textPaintWhite = new Paint();
		textPaintWhite.setAntiAlias(true);
		textPaintWhite.setColor(0xFFFFFFFF);
		textPaintWhite.setTextAlign(Align.LEFT);

		fontOffset = (textPaintGreen.getFontMetrics().ascent + textPaintGreen
				.getFontMetrics().descent) / 2;
		linePaint1 = new Paint();
		linePaint1.setAntiAlias(true);
		linePaint1.setColor(0xFFFFFFFF);
		linePaint1.setTextAlign(Align.LEFT);
		dimentionFound = false;
		setOnTouchListener(this);

	}

	public void setChartData(List<ChartData> points,float largeCellValue) {
		this.points = points;
		this.largeCellValue = largeCellValue;
		graphSrc = new float[2 * points.size() + 2];
		graphDst = new float[graphSrc.length];
		graphSrc[0] = 0;
		graphSrc[1] = 0;
		int j = 2;
		float maxValue = 0;

		for (int i = 0; i < points.size(); i++) {
			graphSrc[j++] = (i + 1) * ITEM_INTERVAL;
			graphSrc[j++] = -UNIT_HEIGHT *(100/largeCellValue)* points.get(i).getValue();
			
			maxValue = Math.max(maxValue, -graphSrc[j-1]);
		}
		lineHeight = Math.max(MIN_HEIGHT,
				(int) (UNIT_HEIGHT*100 * (Math.ceil(maxValue / 100) + 1)));
		lineWidth = (int) Math.max(MIN_WIDTH, ITEM_INTERVAL
				* (points.size() + 2));
		axisSrc = new float[] { -lineWidth, 0, lineWidth, 0, 0, -lineHeight, 0,
				lineHeight };
		axisDst = new float[axisSrc.length];
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		try {
			
				if (!dimentionFound) {
					if (getWidth() > 0 && getHeight() > 0) {
						dimentionFound = true;
						initialOffsetX = getWidth() / 5;
						initialOffsetY = getHeight() * 4 / 5;
					}
				}

				if (dimentionFound &&(graphSrc!=null)&&(graphDst!=null)) {
					matrix.mapPoints(graphDst, graphSrc);
					matrix.mapPoints(axisDst, axisSrc);
					
					float midY = (axisDst[5] + axisDst[7]) / 2;
					float midX = (axisDst[0] + axisDst[2]) / 2;
					float cellHeight = UNIT_HEIGHT*100
							* (axisDst[7] - axisDst[5])
							/ (axisSrc[7] - axisSrc[5]);
					int largeLineCount = (int) ((axisDst[7] - axisDst[5]) / (2 * cellHeight));
			
					float ref2;
					float largeLineBegin = initialOffsetX + midX - 6;
					float largeLineEnd = initialOffsetX + midX + 6;
					float smallLineBegin = initialOffsetX + midX - 3;
					float smallLineEnd = initialOffsetX + midX + 3;
					float textEnd = initialOffsetX + midX - 10;
					for (int i = 0; i <= largeLineCount; i++) {
						if (i == 0) {
							ref2 = initialOffsetY + midY - (i * cellHeight);
							canvas.drawText(Integer.toString(i * 100), textEnd,
									ref2 - fontOffset, textPaintGreen);
						} else {
							// ref1 = initialOffsetY + midY + (i * cellHeight);
							ref2 = initialOffsetY + midY - (i * cellHeight);
							// canvas.drawLine(largeLineBegin,ref1,
							// largeLineEnd,
							// ref1, linePaint1);
							canvas.drawLine(largeLineBegin, ref2, largeLineEnd,
									ref2, linePaint1);

							for (int j = 1; j < 10; j++) {
								// canvas.drawLine(smallLineBegin, ref1 -
								// (cellHeight*j/10) , smallLineEnd,
								// ref1 - (cellHeight*j/10), linePaint1);
								canvas.drawLine(smallLineBegin, ref2
										+ (cellHeight * j / 10), smallLineEnd,
										ref2 + (cellHeight * j / 10),
										linePaint1);
							}
							// canvas.drawText(Integer.toString(-i*100) ,
							// textEnd, ref1- fontOffset, textPaintGreen);
							canvas.drawText(Integer.toString((int)(i * largeCellValue/UNIT_HEIGHT)), textEnd,
									ref2 - fontOffset, textPaintGreen);

						}

					}

					
					Path path = new Path();
					for (int i = 0; i < (graphDst.length - 2) / 2; i++) {
						canvas.drawLine(initialOffsetX + graphDst[2 * i],
								initialOffsetY + graphDst[2 * i + 1],
								initialOffsetX + graphDst[2 * i + 2],
								initialOffsetY + graphDst[2 * i + 3],
								graphPaint);
						path.reset();
						path.moveTo(initialOffsetX + graphDst[2 * i + 2],
								initialOffsetY + graphDst[1]);
						path.lineTo(initialOffsetX + graphDst[2 * i + 2]
								- ITEM_INTERVAL , initialOffsetY
								+ graphDst[1] + 4 * ITEM_INTERVAL);
						canvas.drawTextOnPath(points.get(i).getDateString(),
								path, 10, 0, textPaintWhite);
					}
					canvas.drawLine(initialOffsetX + axisDst[0], initialOffsetY
							+ axisDst[1], initialOffsetX + axisDst[2],
							initialOffsetY + axisDst[3], linePaint1);
					canvas.drawLine(initialOffsetX + axisDst[4], initialOffsetY
							+ axisDst[5], initialOffsetX + axisDst[6],
							initialOffsetY + axisDst[7], linePaint1);

				}

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public boolean onTouch(View v, MotionEvent event) {
		// ImageView view = (ImageView) v;
		View view = v;
		dumpEvent(event);
		switch (event.getAction() & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:
			savedMatrix.set(matrix);
			start.set(event.getX(), event.getY());
			Log.d(TAG, "mode=DRAG");
			mode = DRAG;
			break;
		case MotionEvent.ACTION_POINTER_DOWN:
			oldDist = spacing(event);
			Log.d(TAG, "oldDist=" + oldDist);
			if (oldDist > 10f) {
				savedMatrix.set(matrix);
				midPoint(mid, event);
				mode = ZOOM;
				Log.d(TAG, "mode=ZOOM");
			}
			break;
		case MotionEvent.ACTION_UP:
		case MotionEvent.ACTION_POINTER_UP:
			mode = NONE;
			Log.d(TAG, "mode=NONE");
			break;
		case MotionEvent.ACTION_MOVE:
			if (mode == DRAG) {
				// ...
				matrix.set(savedMatrix);
				matrix.postTranslate(event.getX() - start.x, event.getY()
						- start.y);
			} else if (mode == ZOOM) {
				float newDist = spacing(event);
				Log.d(TAG, "newDist=" + newDist);
				if (newDist > 10f) {
					matrix.set(savedMatrix);
					float scale = newDist / oldDist;
					matrix.postScale(scale, scale, mid.x, mid.y);
				}
			}
			break;
		}

		invalidate();
		return true; // indicate event was handled
	}

	private void dumpEvent(MotionEvent event) {
		String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE",
				"POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };
		StringBuilder sb = new StringBuilder();
		int action = event.getAction();
		int actionCode = action & MotionEvent.ACTION_MASK;
		sb.append("event ACTION_").append(names[actionCode]);
		if (actionCode == MotionEvent.ACTION_POINTER_DOWN
				|| actionCode == MotionEvent.ACTION_POINTER_UP) {
			sb.append("(pid ").append(
					action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
			sb.append(")");
		}
		sb.append("[");
		for (int i = 0; i < event.getPointerCount(); i++) {
			sb.append("#").append(i);
			sb.append("(pid ").append(event.getPointerId(i));
			sb.append(")=").append((int) event.getX(i));
			sb.append(",").append((int) event.getY(i));
			if (i + 1 < event.getPointerCount())
				sb.append(";");
		}
		sb.append("]");
		Log.d(TAG, sb.toString());
	}

	/** Determine the space between the first two fingers */
	private float spacing(MotionEvent event) {
		float x = event.getX(0) - event.getX(1);
		float y = event.getY(0) - event.getY(1);
		return FloatMath.sqrt(x * x + y * y);
	}

	private void midPoint(PointF point, MotionEvent event) {
		float x = event.getX(0) + event.getX(1);
		float y = event.getY(0) + event.getY(1);
		point.set(x / 2, y / 2);
	}
}
